home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-05 / driverss.zip / LOCALTLK.ASM < prev    next >
Assembly Source File  |  1991-01-26  |  32KB  |  1,235 lines

  1. version    equ    2
  2.  
  3.     include    defs.asm
  4.  
  5. ; PC/FTP Packet Driver source, conforming to version 1.09 of the spec
  6. ; Katie Stevens (dkstevens@ucdavis.edu)
  7. ; Computing Services, University of California, Davis
  8. ; Portions (C) Copyright 1988 Regents of the University of California
  9.  
  10. ; This program is free software; you can redistribute it and/or modify
  11. ; it under the terms of the GNU General Public LIcense as published by
  12. ; the Free Software Foundation, version 1.
  13. ;
  14. ; This program is distributed in the hope that it will be useful,
  15. ; but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ; GNU General Public License for more details.
  18.  
  19. code    segment    word public
  20.     assume    cs:code, ds:code
  21.  
  22. ; Definitions specific to the ATALK.SYS driver for PC LocalTalk cards:
  23. ; these include Apple LocalTalk PC Card, Sun/TOPS FlashCard
  24. ; For a complete description of the LocalTalk commands, structures and
  25. ; methods used in this driver, please refer to Apple APDA document #M7055,
  26. ; LocalTalk PC Card and Driver Preliminary Notes.
  27.  
  28. driverstring    db    'AppleTalk', 0        ; ATALK.SYS signature string
  29. dot_char    db    '.', 0            ; for IP address display
  30.  
  31. AT_INT        equ    5CH            ; Software int# for ATALK.SYS
  32.  
  33. ; General ATALK.SYS driver commands
  34. AT_INIT            equ    01H        ; Initialize driver software
  35. AT_GETNETINFO        equ    03H        ; Get driver info
  36.  
  37. ; Datagram Delivery Protocol commands for ATALK.SYS driver
  38. DDP_OPENSOCKET        equ    20H
  39. DDP_CLOSESOCKET        equ    21H
  40. DDP_WRITE        equ    22H
  41. DDP_READ        equ    23H
  42. DDP_CANCEL        equ    24H
  43.  
  44. ; Name Binding Protocol commands for ATALK.SYS driver
  45. NBP_REGISTER        equ    30H
  46. NBP_REMOVE        equ    31H
  47. NBP_LOOKUP        equ    32H
  48. NBP_CONFIRM        equ    33H
  49. NBP_CANCEL        equ    34H
  50.  
  51. ; AppleTalk Transaction Protocol commands for ATALK.SYS driver
  52. ATP_SEND_REQUEST    equ    42H
  53.  
  54. ; ATALK.SYS command qualifiers
  55. ASYNC_MASK    equ    8000H        ; Start command, then return
  56. INTR_MASK    equ    4000H        ; Wait for intr service to complete
  57.  
  58. XO_BIT        equ    20H        ; ATP - exactly once transaction
  59.  
  60. ; Structure for AppleTalk node addressing
  61. AddrBlk struc
  62.     ablk_network    dw    0
  63.     ablk_nodeid    db    0
  64.     ablk_socket    db    0
  65. AddrBlk ends
  66.  
  67. ; Structure for general calls to AppleTalk driver (ATALK.SYS)
  68. InfoParams struc
  69.     atd_command    dw        AT_GETNETINFO
  70.     atd_status    dw        0
  71.     atd_compfun    segmoffs    <>
  72.     inf_network    dw        0
  73.     inf_nodeid    db        0
  74.     inf_abridge    db        0
  75.     inf_config    dw        0
  76.     inf_buffptr    segmoffs    <>
  77.     inf_buffsize    dw        0
  78. InfoParams ends
  79. ; Parameter block for general calls to AppleTalk driver (ATALK.SYS)
  80. MyInfo    InfoParams    <>
  81.  
  82. ; Address block for our gateway
  83. MyGateway    AddrBlk        <>
  84.  
  85. ; Structure for calls to AppleTalk driver (ATALK.SYS) for Datagram
  86. ; Delivery Protocol (DDP) service
  87. DDPParams struc
  88.     ddp_command    dw        0
  89.     ddp_status    dw        0
  90.     ddp_compfun    segmoffs    <>
  91.     ddp_addr    AddrBlk        <>
  92.     ddp_socket    db        0
  93.     ddp_type    db        0
  94.     ddp_buffptr    segmoffs    <>
  95.     ddp_buffsize    dw        0
  96.     ddp_chksum    db        0
  97. DDPParams ends
  98. ; Parameter blocks for AppleTalk DDP access
  99. DDPio        DDPParams    <>        ; Write on DDP socket
  100. ; 2 buffers for packet receive from ATALK.SYS
  101. DDP1inuse    db    0            ; Buffer occupied flag
  102. DDP1buffsize    dw    0            ; Buffer length during reads
  103. DDP1buffer    db    1024 dup (0)        ; Buffer for DDP read
  104. DDP2inuse    db    0            ; 2nd Buffer occupied flag
  105. DDP2buffsize    dw    0            ; 2nd Buffer length during reads
  106. DDP2buffer    db    1024 dup (0)        ; 2nd Buffer for DDP read
  107.  
  108. ; Structure for calls to AppleTalk driver (ATALK.SYS) for Name
  109. ; Binding Protocol (NBP) service
  110. NBPParams struc
  111.     nbp_command    dw        0
  112.     nbp_status    dw        0
  113.     nbp_compfun    segmoffs    <>
  114.     nbp_addr    AddrBlk        <>
  115.     nbp_toget    dw        0
  116.     nbp_buffptr    segmoffs    <>
  117.     nbp_buffsize    dw        0
  118.     nbp_interval    db        0
  119.     nbp_retry    db        0
  120.     nbp_entptr    segmoffs    <>
  121. NBPParams ends
  122. ; Parameter block for AppleTalk NBP access
  123. NBP        NBPParams    <>
  124.  
  125. ; Structure for name-to-address bind entries
  126. NBPTuple struc
  127.     tup_address    AddrBlk        <>
  128.     tup_enum    db        0
  129.     tup_name    db        99 dup(0)
  130. NBPTuple ends
  131. ; Name Binding Tuple for our IP gateway
  132. NBPt        NBPTuple    <>
  133.  
  134. ; Structure for name-to-address table
  135. NBPEntry struc
  136.     tab_next    segmoffs    <>
  137.     tab_entry    NBPTuple    <>
  138. NBPEntry ends
  139. NBPtable    NBPEntry    <>
  140.  
  141. ; Structure for calls to AppleTalk driver (ATALK.SYS) for AppleTalk
  142. ; Transaction Protocol (ATP) service
  143. ATPParams struc
  144.     atp_command    dw        0
  145.     atp_status    dw        0
  146.     atp_compfun    segmoffs    <>
  147.     atp_addrblk    AddrBlk        <>
  148.     atp_socket    db        0
  149.     atp_fill    db        0
  150.     atp_buffptr    segmoffs    <>
  151.     atp_buffsize    dw        0
  152.     atp_interval    db        0
  153.     atp_retry    db        0
  154.     atp_flags    db        0
  155.     atp_seqbit    db        0
  156.     atp_tranid    dw        0
  157.     atp_userbytes    db        4 dup(0)
  158.     atp_bdsbuffs    db        0
  159.     atp_bdsresps    db        0
  160.     atp_bdsptr    segmoffs    <>
  161. ATPParams ends
  162. ; Parameter block for AppleTalk ATP access
  163. ATP        ATPParams    <>
  164.  
  165. ; Structure for BDS elements
  166. BDSElement struc
  167.     bds_buffptr    segmoffs    <>
  168.     bds_buffsize    dw        0
  169.     bds_datasize    dw        0
  170.     bds_userbytes    db        4 dup(0)
  171. BDSElement ends
  172. ; Parameter block for our BDS element
  173. BDS        BDSElement    <>
  174.  
  175. ; Struct for IP gateway information
  176. IPGInfo struc
  177.     ipg_opcode    db        0,0,0,1    ; IPGP_ASSIGN
  178.     ipg_ipaddress    dd        0    ; our IP address
  179.     ipg_ipname    dd        0    ; nameserver IP address
  180.     ipg_ipbroad    dd        0    ; broadcast IP address
  181.     ipg_ipfile    dd        0    ; file server IP address
  182.     ipg_ipother    dd        4 dup (0)
  183.     ipg_string    db        128 dup (0), '$'
  184. IPGInfo ends
  185. ; Parameter block for info about our IP gateway
  186. IPG        IPGInfo        <>
  187.  
  188. IPG_ERROR    equ    -1
  189.  
  190. static_address    db    0, 0, 0, 0
  191. use_static    db    0
  192. test_address    db    0, 0, 0, 0
  193. temp_4bytes    db    0, 0, 0, 0
  194.  
  195. ; End of Appletalk parameter definitions
  196.  
  197. ; The following values may be overridden from the command line.
  198. ; If they are omitted from the command line, these defaults are used.
  199.  
  200.     public    int_no
  201. int_no    db    0,0,0,0            ;must be four bytes long for get_number.
  202.  
  203.     public    driver_class, driver_type, driver_name, driver_function, parameter_list
  204. driver_class    db    5,0        ;from the packet spec
  205. driver_type    db    1        ;from the packet spec
  206. driver_name    db    'LocalTalk',0    ;name of the driver.
  207. driver_function    db    2
  208. parameter_list    label    byte
  209.     db    1    ;major rev of packet driver
  210.     db    9    ;minor rev of packet driver
  211.     db    14    ;length of parameter list
  212.     db    EADDR_LEN    ;length of MAC-layer address
  213.     dw    GIANT    ;MTU, including MAC headers
  214.     dw    MAX_MULTICAST * EADDR_LEN    ;buffer size of multicast addrs
  215.     dw    0    ;(# of back-to-back MTU rcvs) - 1
  216.     dw    0    ;(# of successive xmits) - 1
  217. int_num    dw    0    ;Interrupt # to hook for post-EOI
  218.             ;processing, 0 == none,
  219.  
  220.     public    rcv_modes
  221. rcv_modes    dw    4        ;number of receive modes in our table.
  222.         dw    0,0,0,rcv_mode_3
  223.  
  224.     public    as_send_pkt
  225. ; The Asynchronous Transmit Packet routine.
  226. ; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
  227. ;   interrupts possibly enabled.
  228. ; Exit with nc if ok, or else cy if error, dh set to error number.
  229. ;   es:di and interrupt enable flag preserved on exit.
  230. as_send_pkt:
  231.     ret
  232.  
  233.     public    drop_pkt
  234. ; Drop a packet from the queue.
  235. ; Enter with es:di -> iocb.
  236. drop_pkt:
  237.     assume    ds:nothing
  238.     ret
  239.  
  240.     public    xmit
  241. ; Process a transmit interrupt with the least possible latency to achieve
  242. ;   back-to-back packet transmissions.
  243. ; May only use ax and dx.
  244. xmit:
  245.     assume    ds:nothing
  246.     ret
  247.  
  248.     public    send_pkt
  249. send_pkt:
  250. ;enter with es:di->upcall routine, (0:0) if no upcall is desired.
  251. ;  (only if the high-performance bit is set in driver_function)
  252. ;enter with ds:si -> packet, cx = packet length.
  253. ;if we're a high-performance driver, es:di -> upcall.
  254. ;exit with nc if ok, or else cy if error, dh set to error number.
  255.     assume    ds:nothing
  256. ; send packet to AppleTalk/DDP/IP gateway
  257.     ; load info about the packet we are sending
  258.     mov    DDPio.ddp_buffptr.offs, si
  259.     mov    DDPio.ddp_buffptr.segm, ds    ; DDPio.buffptr -> IP packet
  260.     mov    DDPio.ddp_buffsize, cx        ; DDPio.buffsize = packet len
  261.  
  262.     ; send all packets to the IP gateway
  263.     mov    cx, (size AddrBlk)        ; DDPio.ddp_addr = MyGateway
  264.     mov    ax, cs
  265.     mov    ds, ax
  266.     mov    es, ax
  267.     mov    si, offset MyGateway
  268.     mov    di, offset DDPio.ddp_addr
  269.     rep    movsb
  270.  
  271.     mov    bx, offset DDPio
  272.     call    doATint                ; Ask ATALK.SYS to send packet
  273.  
  274.     cmp    DDPio.ddp_status, 00H        ; Packet sent okay?
  275.     je    send_ret            ; Yes, status is good
  276.                         ; No, status gives error
  277. send_err:
  278.     call    count_out_err
  279.     mov    dh, CANT_SEND            ; set error flag
  280.     stc
  281.     ret
  282.  
  283. send_ret:
  284.     clc                    ; packet sent successfully
  285.     ret
  286.  
  287.     public    get_address
  288. get_address:
  289. ;get the address of the interface.
  290. ;enter with es:di -> place to get the address, cx = size of address buffer.
  291. ;exit with nc, cx = actual size of address, or cy if buffer not big enough.
  292.     assume    ds:code
  293.     ret
  294.  
  295.  
  296.     public    set_address
  297. set_address:
  298. ;enter with ds:si -> Ethernet address, CX = length of address.
  299. ;exit with nc if okay, or cy, dh=error if any errors.
  300.     assume    ds:nothing
  301.     mov    dh, BAD_COMMAND
  302.     stc
  303.     ret
  304.  
  305.  
  306. rcv_mode_3:
  307. ;receive mode 3 is the only one we support, so we don't have to do anything.
  308.     ret
  309.  
  310.  
  311.     public    set_multicast_list
  312. set_multicast_list:
  313. ;enter with ds:si ->list of multicast addresses, cx = number of addresses.
  314. ;return nc if we set all of them, or cy,dh=error if we didn't.
  315.     mov    dh,NO_MULTICAST
  316.     stc
  317.     ret
  318.  
  319.  
  320.     public    terminate
  321. terminate:
  322.     push    ds
  323.     push    cs
  324.     pop    ds
  325.  
  326. terminate_write:
  327.     ; close the DDP socket
  328.     mov    DDPio.ddp_command, DDP_CLOSESOCKET
  329.     mov    bx, offset DDPio
  330.     call    doATint
  331.  
  332.     mov    NBP.nbp_command, NBP_REMOVE
  333.     mov    NBP.nbp_entptr.offs, offset NBPtable.tab_entry.tup_name
  334.     mov    NBP.nbp_entptr.segm, ds
  335.     mov    bx, offset NBP
  336.     call    doATint
  337.  
  338.     pop    ds
  339.     ret
  340.  
  341.     public    reset_interface
  342. reset_interface:
  343. ;reset the interface.
  344.     assume    ds:code
  345.     ret
  346.  
  347.  
  348. ;called when we want to determine what to do with a received packet.
  349. ;enter with cx = packet length, es:di -> packet type, dl = packet class.
  350.     extrn    recv_find: near
  351.  
  352. ;called after we have copied the packet into the buffer.
  353. ;enter with ds:si ->the packet, cx = length of the packet.
  354.     extrn    recv_copy: near
  355.  
  356.     extrn    count_in_err: near
  357.     extrn    count_out_err: near
  358.  
  359.     public    recv
  360. recv:
  361. ;called from the recv isr.  All registers have been saved, and ds=cs.
  362. ;Upon exit, the interrupt will be acknowledged.
  363. ;NOTE: this packet driver merely makes calls to another hardware
  364. ;driver, ATALK.SYS. ATALK.SYS handles the hardware interrupt service;
  365. ;ATALK.SYS then calls this packet driver with FAR subroutine calls.
  366. ;the ATALK.SYS FAR subroutine is recv_at_upcall
  367.     assume    ds:nothing
  368.     ret
  369.  
  370.     public    recv_exiting
  371. recv_exiting:
  372. ;called from the recv isr after interrupts have been acknowledged.
  373. ;Only ds and ax have been saved.
  374.     assume    ds:nothing
  375.     ret
  376.  
  377. ;*******************************************
  378.  
  379. ; NULL completion routine for ATALK.SYS drivers calls
  380. noop_upcall proc far
  381.     ret
  382. noop_upcall endp
  383.  
  384. ;First half routine for DDP socket.
  385. ;ATALK.SYS calls this routine when a packet is received.
  386. ;ATALK.SYS assumes we are a far procedure.
  387. ;    CX = size of data packet
  388. preview_upcall proc far
  389.     assume    ds:nothing
  390.  
  391. ;    maximum packet we can receive is 1024 bytes
  392.     cmp    cx, 1024
  393.     ja    preview_drop
  394.  
  395. preview_buff1:
  396.     cmp    DDP1inuse, 00H
  397.     jne    preview_buff2
  398.     mov    DDP1inuse, 01H
  399.  
  400. ;    repeat buffer size back to ATALK.SYS in CX
  401. ;    ask ATALK.SYS driver to pass us the buffer at DS:BX
  402. ;    tell ATALK.SYS address of 2nd half routine in ES:DX
  403.     push    cs
  404.     pop    ds
  405.     mov    bx, offset DDP1buffer        ; ds:bx->buffer
  406.     push    cs
  407.     pop    es
  408.     mov    dx, offset recv_at_upcall    ; es:dx->2nd half routine
  409.     jmp    preview_ret
  410.  
  411. preview_buff2:
  412.     cmp    DDP2inuse, 00H
  413.     jne    preview_drop
  414.     mov    DDP2inuse, 01H
  415.  
  416. ;    repeat buffer size back to ATALK.SYS in CX
  417. ;    ask ATALK.SYS driver to pass us the buffer at DS:BX
  418. ;    tell ATALK.SYS address of 2nd half routine in ES:DX
  419.     push    cs
  420.     pop    ds
  421.     mov    bx, offset DDP2buffer        ; ds:bx->buffer
  422.     push    cs
  423.     pop    es
  424.     mov    dx, offset recv_at_upcall    ; es:dx->2nd half routine
  425.     jmp    preview_ret
  426.  
  427.  
  428. preview_drop:
  429. ;    ask ATALK.SYS to drop the packet
  430.     call    count_in_err
  431.     mov    cx, 00h
  432.  
  433. preview_ret:
  434.     ret
  435. preview_upcall endp
  436.  
  437. ;Second half routine for DDP socket.
  438. ;ATALK.SYS calls this routine when the packet has been copied to our buffer.
  439. ;ATALK.SYS assumes we are a far procedure.
  440. ;    CX = size of data packet
  441. ;    DS:BX = address of buffer
  442. recv_at_upcall proc far
  443.     assume    ds:nothing
  444.  
  445. recv_buff1:
  446.     cmp    bx, offset DDP1buffer
  447.     jne    recv_buff2
  448.  
  449. ;    check if we have a client waiting for packets
  450. ;    pass to recv_find    es:di->driver_type, cx=#bytes in packet
  451.     mov    DDP1buffsize, cx
  452.     mov    di, offset driver_type
  453.     push    cs
  454.     pop    es
  455.     mov    dl,cs:driver_class
  456.     call    recv_find
  457.  
  458. ;    es:di->client buffer, or es:di=0 means drop the packet
  459.     mov    ax, es
  460.     or    ax, di
  461.     je    recv_pass1
  462.  
  463. ;    copy ds:si->es:di for cx bytes
  464.     push    cs
  465.     pop    ds
  466.     mov    si, offset DDP1buffer
  467.     mov    cx, DDP1buffsize
  468.     rep    movsb
  469.  
  470. ;    tell receiver copy has been made; ds:si->the packet, cx=length
  471.     push    es
  472.     pop    ds
  473.     mov    si, offset DDP1buffer
  474.     mov    cx, DDP1buffsize
  475.     call    recv_copy
  476.  
  477. recv_pass1:
  478. ;    first buffer is free for use again
  479.     mov    DDP1inuse, 00H
  480.     jmp    recv_ret
  481.  
  482.  
  483. recv_buff2:
  484.     cmp    bx, offset DDP2buffer
  485.     jne    recv_ret
  486.  
  487. ;    check if we have a client waiting for packets
  488. ;    pass to recv_find    es:di->driver_type, cx=#bytes in packet
  489.     mov    DDP2buffsize, cx
  490.     mov    di, offset driver_type
  491.     push    cs
  492.     pop    es
  493.     call    recv_find
  494.  
  495. ;    es:di->client buffer, or es:di=0 means drop the packet
  496.     mov    ax, es
  497.     or    ax, di
  498.     je    recv_pass2
  499.  
  500. ;    copy ds:si->es:di for cx bytes
  501.     push    cs
  502.     pop    ds
  503.     mov    si, offset DDP2buffer
  504.     mov    cx, DDP2buffsize
  505.     rep    movsb
  506.  
  507. ;    tell receiver copy has been made; ds:si->the packet, cx=length
  508.     push    es
  509.     pop    ds
  510.     mov    si, offset DDP2buffer
  511.     mov    cx, DDP2buffsize
  512.     call    recv_copy
  513. recv_pass2:
  514. ;    second buffer is now free for use
  515.     mov    DDP2inuse, 00H
  516.  
  517. recv_ret:
  518.     ret
  519. recv_at_upcall endp
  520.  
  521. ;*******************************************
  522.  
  523. ; Call DOS software interrupt for AppleTalk
  524. ; caller must set ds:bx -> parameter block for ATALK.SYS
  525. doATint:
  526.     int    AT_INT                ; Interrupt ATALK.SYS driver
  527.     ret
  528.  
  529.  
  530.  
  531. ;any code after this will not be kept after initialization.
  532. end_resident    label    byte
  533. ;****************************************************************************
  534.  
  535.     public    usage_msg
  536. usage_msg    db    "usage: localtlk [-n] [-d] [-w] <packet_int_no>",CR,LF,'$'
  537.  
  538.     public    copyright_msg
  539. copyright_msg    db    "Packet driver for Apple LocalTalk PC Card, version ",'0'+majver,".",'0'+version,CR,LF
  540.         db    "Portions Copyright 1990, Regents of the University of California",CR,LF,'$'
  541.  
  542. no_atalk_sys_msg:
  543.     db    "Couldn't locate ATALK.SYS -- packet driver not installed",CR,LF,'$'
  544. atalk_sys_found_msg:
  545.     db    "ATALK.SYS driver located at software interrupt ",'$'
  546. inf_nodeid_name:
  547.     db    "Attaching to AppleTalk network as node ",'$'
  548. inf_abridge_name:
  549.     db    "AppleTalk network bridge is node ",'$'
  550. ddp_failed_msg:
  551.     db    "Datagram Delivery Protocol socket open failed; return status: ",'$'
  552. ddp_wrong_socket_msg:
  553.     db    "Datagram Delivery Protocol failed; unable to aquire requested socket",CR,LF,'$'
  554. ddp_open_msg:
  555.     db    "Datagram Delivery Protocol open on socket ",'$'
  556. atalk_open_msg:
  557.     db    "Attached to AppleTalk network as (net:node:sock): ",'$'
  558. nbp_no_gateway_msg:
  559.     db    "NBP: IPGATEWAY lookup failed; return status: ",'$'
  560. nbp_ipg_addr_msg:
  561.     db    "IPGATEWAY located on AppleTalk network as (net:node:sock): ",'$'
  562. atp_no_gateway_msg:
  563.     db    "ATP: IPGATEWAY transport setup failed; return status: ",'$'
  564. ipg_gateway_err_msg:
  565.     db    "IP Gateway error: ",'$'
  566. myip_addr_msg:
  567.     db    "My IP address: ",'$'
  568. ns_ip_addr_msg:
  569.     db    "Name Server IP address: ",'$'
  570. bd_ip_addr_msg:
  571.     db    "Broadcast IP address: ",'$'
  572. fs_ip_addr_msg:
  573.     db    "File Server IP address: ",'$'
  574. opcode_msg:
  575.     db    "IPG opcode: ",'$'
  576. nbp_no_register_msg:
  577.     db    "NBP: failed, couldn't register our name; return status: ",'$'
  578. ddp_cant_recv:
  579.     db    "DDP: couldn't initiate read on socket; return status: ",'$'
  580. test_arg_msg:
  581.     db    "Test IP arg parsing: ",'$'
  582.  
  583.  
  584. ipgateway_name:
  585.     db    01H, '=', 09H, "IPGATEWAY", 01H, '*', '0'
  586. myip_name:
  587.     db    09H, "IPADDRESS", 01H, '*', '0'
  588. myip_name_len    equ    12
  589.  
  590. ; Temporary storage for calls to print_number
  591. dtemp    dw    ?
  592.     dw    0
  593.  
  594.  
  595.     extrn    set_recv_isr: near
  596.  
  597. ;enter with si -> argument string, di -> wword to store.
  598. ;if there is no number, don't change the number.
  599.     extrn    get_number: near
  600.  
  601. ;enter with ds:dx -> argument string, ds:di -> dword to print.
  602.     extrn    print_number: near
  603.  
  604. ;enter with al = char to display
  605.     extrn    chrout: near
  606. ;enter with ax,dx holding 32 bits to display in decimal (ax holds low word)
  607.     extrn    decout: near
  608.     extrn    byteout: near
  609.     extrn    wordout: near
  610.  
  611.     extrn    skip_blanks: near
  612.  
  613. ;enter with si -> argument string, di -> word to store.
  614. ;if there is no number, don't change the number.
  615.     extrn    get_number: near
  616.  
  617. ;called with ds:si -> immediately after the packet_int_no
  618.     public    parse_args
  619. parse_args:
  620.     call    skip_blanks
  621.     lodsb
  622.     cmp    al, CR
  623.     je    no_more_args
  624.     cmp    al, '['                ; check for square brackets
  625.     je    past_brackets
  626.     dec    si                ; not a bracket, back up
  627. past_brackets:
  628.     mov    di, offset temp_4bytes        ; get first IP address byte
  629.     call    get_number 
  630.     mov    byte ptr test_address, cl
  631.     lodsb
  632.     cmp    al, '.'
  633.     jne    no_more_args
  634.  
  635.     mov    di, offset temp_4bytes        ; get second IP address byte
  636.     call    get_number 
  637.     mov    byte ptr test_address+1, cl
  638.     lodsb
  639.     cmp    al, '.'
  640.     jne    no_more_args
  641.  
  642.     mov    di, offset temp_4bytes        ; get third IP address byte
  643.     call    get_number 
  644.     mov    byte ptr test_address+2, cl
  645.     lodsb
  646.     cmp    al, '.'
  647.     jne    no_more_args
  648.  
  649.     mov    di, offset temp_4bytes        ; get first IP address byte
  650.     call    get_number 
  651.     mov    byte ptr test_address+3, cl
  652.  
  653. ;    mov    dx, offset test_arg_msg
  654. ;    mov    di, offset test_address
  655. ;push    si
  656. ;    call    print_ip_addr
  657. ;pop    si
  658.  
  659.     mov    ax, word ptr test_address
  660.     mov    word ptr static_address, ax
  661.     mov    ax, word ptr test_address+2
  662.     mov    word ptr static_address+2, ax
  663.     mov    use_static, 01H
  664.  
  665.     lodsb
  666.     cmp    al, ']'
  667.     je    arg_return
  668.  
  669. ;exit with nc if all went well, cy otherwise.
  670. no_more_args:
  671.     dec    si
  672. arg_return:
  673.     clc
  674.     ret
  675.  
  676.  
  677. ; Initialize our interface to the ATALK.SYS driver.
  678. ; NOTE: this initialization code is modeled after the PC/IP LocalTalk
  679. ; driver written by Dan Lanciani (ddl@harvard.harvard.edu); the PCIP
  680. ; software package can found at husc6.harvard.edu:pub/pcip/pcip.tar.Z
  681.     public    etopen
  682. etopen:
  683.     assume    ds:code
  684.  
  685. ; ATALK.SYS driver may be loaded at a software interrupt somewhere
  686. ; between 5CH and 70H. Locate ATALK.SYS driver by scanning for signature.
  687. isATLoaded:                    ; Look for ATALK.SYS driver
  688.     cld
  689.     call    ATGetInt            ; Load start of intr range
  690.     mov    dx, ax                ; Save start value in DX
  691. chkloop:
  692.     cmp    dx, 70H                ; Scanned all possible vectors?
  693.     jne    checkstring            ; No, check this vector
  694.     xor    ax, ax                ; Yes, driver not found
  695.     jmp    chksplit            ; Skip ahead to return
  696. checkstring:
  697.     mov    bx, dx                ; Load intr# for scan
  698.     shl    bx, 1                ; Multiply by 2 (for seg bytes)
  699.     shl    bx, 1                ; Multiply by 2 (for off bytes)
  700.     xor    ax, ax
  701.     mov    es, ax                ; Lowest page of memory
  702.     lds    si, es:[bx]            ; Load vector for scan intr#
  703.     mov    ax, ds                ; Load segment this scan intr#
  704.     or    ax, si                ; OR with off this scan intr#
  705.     jz    keepchecking            ; Keep checking if no bits
  706.     sub    si, 16                ; Signature is just before code
  707.     mov    di, offset driverstring        ; Load compare string
  708.     mov    cx, 9                ; Load length of compare string
  709.     push    cs
  710.     pop    es
  711.     repe    cmpsb                ; Compare ds:si to es:di
  712.     jne    keepchecking            ; Keep checking if not matched
  713.     call    ATGetInt            ; Matched, get INT# again
  714.     cmp    ax, dx                ; INT# already set properly?
  715.     jz    chksplit            ; Yes, use this INT#
  716.                         ; No, we found INT# by scanning
  717.     call    ATPatch                ; Modify code to match scan
  718.     call    ATGetInt            ; Retrieve final INT#
  719.     jmp    chksplit            ; Skip ahead to return
  720.  
  721. keepchecking:                    ; Havent found ATALK.SYS driver
  722.     inc    dx                ; Check next possible INT#
  723.     jmp    chkloop                ; Loop back to check next INT#
  724.  
  725. chksplit:                    ; Done with scan for ATALK.SYS
  726.     cmp    ax, 00H                ; ATALK.SYS driver found?
  727.     jne    atalk_sys_found            ; Yes, skip ahead to continue
  728.  
  729.     mov    dx, offset no_atalk_sys_msg    ; No, ATALK.SYS not loaded
  730.     jmp    error_wrt            ; Skip ahead to report error
  731.  
  732. atalk_sys_found:                ; ATALK.SYS driver found
  733.     push    cs                ; Used DS for another purpose
  734.     pop    ds                ; Reset DS to our data
  735.     mov    dtemp, dx            ; Report intr# of ATALK.SYS
  736.     mov    di, offset dtemp
  737.     mov    dx, offset atalk_sys_found_msg
  738.     call    print_number
  739.  
  740. ; We need to establish our Appletalk node
  741. get_our_info:                    ; Get info params from ATALK
  742.     mov    MyInfo.atd_command, AT_GETNETINFO
  743.     mov    MyInfo.atd_compfun.offs, offset noop_upcall
  744.     mov    MyInfo.atd_compfun.segm, cs
  745.     mov    bx, offset MyInfo
  746.     call    doATint
  747.  
  748.     cmp    MyInfo.atd_status, 00H        ; Already initialized?
  749.     je    get_ddp_socket            ; Yes, skip ahead
  750.  
  751.     mov    MyInfo.atd_command, AT_INIT    ; No, initialize our node
  752.     mov    MyInfo.atd_compfun.offs, offset noop_upcall
  753.     mov    MyInfo.atd_compfun.segm, cs
  754.     mov    bx, offset MyInfo
  755.     call    doATint
  756.  
  757. ; We need to establish our AppleTalk/DDP socket
  758. get_ddp_socket:                    ; Open a DDP socket
  759.     mov    DDPio.ddp_command, DDP_OPENSOCKET
  760.     mov    DDPio.ddp_compfun.offs, offset noop_upcall
  761.     mov    DDPio.ddp_compfun.segm, cs
  762.     mov    DDPio.ddp_buffptr.offs, offset preview_upcall
  763.     mov    DDPio.ddp_buffptr.segm, cs
  764.     mov    DDPio.ddp_socket, 72        ; ask for experimental sock#
  765.     mov    DDPio.ddp_type, 22        ; ask for IP socket type
  766.     mov    bx, offset DDPio            ; ds:bx-> DDP param block
  767.     call    doATint                ; ask ATALK.SYS for a socket
  768.  
  769.     cmp    DDPio.ddp_status, 00H        ; error return from ATALK.SYS?
  770.     je    chk_ddp_socket            ; no, skip ahead to continue
  771.                         ; yes, no socket for us
  772.     mov    ax, DDPio.ddp_status
  773.     mov    dtemp, ax
  774.     mov    di, offset dtemp
  775.     mov    dx, offset ddp_failed_msg
  776.     call    print_number            ; report error and stat return
  777.     jmp    error_ret
  778.  
  779. ;**** do we really require socket 72?
  780. chk_ddp_socket:                    ; check the socket we opened
  781.     cmp    DDPio.ddp_socket, 72        ; did we get the one requested?
  782.     je    ddp_ready            ; yes, socket is as expected
  783.                         ; no, but must have socket 72
  784.     mov    DDPio.ddp_command, DDP_CLOSESOCKET
  785.     mov    bx, offset DDPio
  786.     call    doATint                ; close the assigned socket
  787.  
  788.     mov    dx, offset ddp_wrong_socket_msg    ; load error msg
  789.     jmp    error_wrt            ; skip ahead to display and ret
  790.  
  791. ddp_ready:                    ; DDP socket 72 is ready
  792.     mov    DDPio.ddp_command, DDP_WRITE    ; Use param block for WRITE now
  793.  
  794. ; AppleTalk node and DDP socket have been established
  795.     mov    ax, MyInfo.inf_network
  796.     mov    word ptr dtemp, ax
  797.     mov    al, MyInfo.inf_nodeid
  798.     mov    ah, DDPio.ddp_socket
  799.     mov    byte ptr dtemp+2, al
  800.     mov    byte ptr dtemp+3, ah
  801.     mov    di, offset dtemp
  802.     mov    dx, offset atalk_open_msg
  803.     call    print_at_addr            ; display AppleTalk node info
  804.     mov    ax, 00H
  805.     mov    dtemp+2, ax
  806.  
  807. ; We need an IP gateway node
  808. nbp_ipgateway:                    ; Locate our IP gateway node
  809.     push    cs
  810.     pop    ds
  811.     mov    NBP.nbp_command, NBP_LOOKUP
  812.     mov    NBP.nbp_compfun.offs, offset noop_upcall
  813.     mov    NBP.nbp_compfun.segm, cs
  814.     mov    NBP.nbp_toget, 01H
  815.     mov    NBP.nbp_buffptr.offs, offset NBPt
  816.     mov    NBP.nbp_buffptr.segm, ds
  817.     mov    NBP.nbp_buffsize, (size NBPTuple)
  818.     mov    NBP.nbp_interval, 5
  819.     mov    NBP.nbp_retry, 12
  820.     mov    NBP.nbp_entptr.offs, offset ipgateway_name
  821.     mov    NBP.nbp_entptr.segm, ds
  822.     mov    bx, offset NBP
  823.     call    doATint                ; do name-bind lookup
  824.  
  825.     cmp    NBP.nbp_status, 00H        ; status return=error?
  826.     jne    nbp_no_gateway            ; yes, report error and exit
  827.  
  828.     cmp    NBP.nbp_toget, 01H
  829.     je    atp_setup
  830.  
  831. nbp_no_gateway:                    ; NBP lookup failed
  832.     mov    ax, NBP.nbp_status
  833.     mov    dtemp, ax
  834.     mov    di, offset dtemp
  835.     mov    dx, offset nbp_no_gateway_msg    ; display error msg
  836.     call    print_number
  837.  
  838.     mov    DDPio.ddp_command, DDP_CLOSESOCKET
  839.     mov    bx, offset DDPio
  840.     call    doATint                ; close the assigned socket
  841.  
  842.     jmp    error_ret            ; skip ahead to return
  843.  
  844. ; We need a transport layer to the IP gateway
  845. atp_setup:
  846.     mov    cx, (size AddrBlk)        ; MyGateway = NBPt.tup_addr
  847.     push    cs
  848.     pop    es
  849.     mov    si, offset NBPt.tup_address
  850.     mov    di, offset MyGateway
  851.     rep    movsb
  852.  
  853.     mov    di, offset NBPt.tup_address    ; Display our gateway node
  854.     mov    dx, offset nbp_ipg_addr_msg
  855.     call    print_at_addr
  856.  
  857.     mov    BDS.bds_buffptr.offs, offset IPG
  858.     mov    BDS.bds_buffptr.segm, ds
  859.     mov    BDS.bds_buffsize, (size IPGInfo)
  860.  
  861.     mov    ATP.atp_command, ATP_SEND_REQUEST
  862.     mov    ATP.atp_compfun.offs, offset noop_upcall
  863.     mov    ATP.atp_compfun.segm, cs
  864.     mov    cx, (size AddrBlk)        ; ATP.atp_addr = NBPt.tup_addr
  865.     push    cs
  866.     pop    es
  867.     mov    si, offset NBPt.tup_address
  868.     mov    di, offset ATP.atp_addrblk
  869.     rep    movsb
  870.     mov    ATP.atp_buffptr.offs, offset IPG
  871.     mov    ATP.atp_buffptr.segm, ds
  872.     mov    ATP.atp_buffsize, (size IPGInfo)
  873.     mov    ATP.atp_interval, 05H
  874.     mov    ATP.atp_retry, 05H
  875.     mov    ATP.atp_flags, XO_BIT
  876.     mov    ATP.atp_bdsbuffs, 01H
  877.     mov    ATP.atp_bdsptr.offs, offset BDS
  878.     mov    ATP.atp_bdsptr.segm, ds
  879.     mov    bx, offset ATP
  880.     call    doATint
  881.  
  882.     cmp    ATP.atp_status, 00H        ; status return=error?
  883.     jne    atp_no_gateway            ; yes, report error and exit
  884.  
  885.     cmp    ATP.atp_bdsbuffs, 01H
  886.     je    chk_ip_opcode
  887.  
  888. atp_no_gateway:                    ; ATP setup failed
  889.     mov    ax, ATP.atp_status
  890.     mov    dtemp, ax
  891.     mov    di, offset dtemp
  892.     mov    dx, offset atp_no_gateway_msg    ; display error msg
  893.     call    print_number
  894.  
  895.     mov    DDPio.ddp_command, DDP_CLOSESOCKET
  896.     mov    bx, offset DDPio
  897.     call    doATint                ; close the assigned socket
  898.  
  899.     jmp    error_ret            ; skip ahead to return
  900.  
  901. chk_ip_opcode:
  902.     cmp    IPG.ipg_opcode.offs, IPG_ERROR    ; opcode is 32 bit
  903.     jne    save_ipaddr            ; check one word at a time
  904.     cmp    IPG.ipg_opcode.segm, IPG_ERROR    ; error from IP gateway?
  905.     jne    save_ipaddr            ; no, transport established
  906.                         ; yes, ATP setup failed
  907.     mov    dx, offset ipg_gateway_err_msg    ; display IPG error msg
  908.     mov    ah, 9
  909.     int    21H
  910.     mov    dx, offset IPG.ipg_string
  911.     mov    ah, 9
  912.     int    21H
  913.     mov    al, 13                ; display CR-LF
  914.     call    chrout
  915.     mov    al, 10
  916.     call    chrout
  917.  
  918.     mov    DDPio.ddp_command, DDP_CLOSESOCKET
  919.     mov    bx, offset DDPio
  920.     call    doATint                ; close the assigned socket
  921.  
  922.     jmp    error_ret
  923.  
  924. ; AppleTalk/IP transport layer established
  925. save_ipaddr:
  926.     mov    dx, offset myip_addr_msg
  927.     cmp    use_static, 00H
  928.     jne    show_static
  929. show_dynamic:
  930.     mov    di, offset IPG.ipg_ipaddress
  931.     jmp    show_ipaddr
  932. show_static:
  933.     mov    di, offset static_address
  934. show_ipaddr:
  935.     call    print_ip_addr
  936.  
  937.     mov    dx, offset ns_ip_addr_msg
  938.     mov    di, offset IPG.ipg_ipname
  939.     call    print_ip_addr
  940.  
  941.     mov    dx, offset bd_ip_addr_msg
  942.     mov    di, offset IPG.ipg_ipbroad
  943.     call    print_ip_addr
  944.  
  945.     mov    dx, offset fs_ip_addr_msg
  946.     mov    di, offset IPG.ipg_ipfile
  947.     call    print_ip_addr
  948.  
  949. ; We need to register ourself with the AppleTalk Name Binding Agent
  950. nbp_register_ourself:
  951.     mov    al, MyInfo.inf_nodeid
  952.     mov    NBPtable.tab_entry.tup_address.ablk_nodeid, al
  953.     mov    al, DDPio.ddp_socket
  954.     mov    NBPtable.tab_entry.tup_address.ablk_socket, al
  955.  
  956.     ; print our IP address in our NBP table entry
  957.     mov    bx, offset NBPtable.tab_entry.tup_name
  958.     inc    bx
  959.     xor    dx, dx
  960.     cmp    use_static, 00H
  961.     jne    reg_static1
  962. reg_dynamic1:
  963.     mov    dl, byte ptr IPG.ipg_ipaddress
  964.     jmp    reg_format1
  965. reg_static1:
  966.     mov    dl, byte ptr static_address
  967. reg_format1:
  968.     call    decstr
  969.     mov    al, dot_char
  970.     mov    byte ptr ds:[bx], al
  971.     inc    bx
  972.     cmp    use_static, 00H
  973.     jne    reg_static2
  974. reg_dynamic2:
  975.     mov    dl, byte ptr IPG.ipg_ipaddress+1
  976.     jmp    reg_format2
  977. reg_static2:
  978.     mov    dl, byte ptr static_address+1
  979. reg_format2:
  980.     call    decstr
  981.     mov    al, dot_char
  982.     mov    ds:[bx], al
  983.     inc    bx
  984.     cmp    use_static, 00H
  985.     jne    reg_static3
  986. reg_dynamic3:
  987.     mov    dl, byte ptr IPG.ipg_ipaddress+2
  988.     jmp    reg_format3
  989. reg_static3:
  990.     mov    dl, byte ptr static_address+2
  991. reg_format3:
  992.     call    decstr
  993.     mov    al, dot_char
  994.     mov    ds:[bx], al
  995.     inc    bx
  996.     cmp    use_static, 00H
  997.     jne    reg_static4
  998. reg_dynamic4:
  999.     mov    dl, byte ptr IPG.ipg_ipaddress+3
  1000.     jmp    reg_format4
  1001. reg_static4:
  1002.     mov    dl, byte ptr static_address+3
  1003. reg_format4:
  1004.     call    decstr
  1005.  
  1006.     mov    ax, bx
  1007.     sub    ax, offset NBPtable.tab_entry.tup_name
  1008.     sub    ax, 1
  1009.     mov    NBPtable.tab_entry.tup_name, al
  1010.  
  1011.     mov    cx, myip_name_len    ; append IPADDR command to our IP
  1012.     push    cs
  1013.     pop    es
  1014.     mov    si, offset myip_name    ; ds:si -> source
  1015.     mov    di, bx            ; es:di -> dest
  1016.     rep    movsb
  1017.  
  1018.     ; Register our name with NBP agent
  1019.     mov    NBP.nbp_command, NBP_REGISTER
  1020.     mov    NBP.nbp_compfun.offs, offset noop_upcall
  1021.     mov    NBP.nbp_compfun.segm, cs
  1022.     mov    NBP.nbp_buffptr.offs, offset NBPtable
  1023.     mov    NBP.nbp_buffptr.segm, ds
  1024.     mov    NBP.nbp_interval, 01H
  1025.     mov    NBP.nbp_retry, 03H
  1026.     mov    bx, offset NBP
  1027.     call    doATint
  1028.  
  1029.     cmp    NBP.nbp_status, 00H
  1030.     je    atinit_done
  1031.  
  1032. nbp_no_register:
  1033.     mov    ax, NBP.nbp_status
  1034.     mov    dtemp, ax
  1035.     mov    di, offset dtemp
  1036.     mov    dx, offset nbp_no_register_msg    ; display error msg
  1037.     call    print_number
  1038.  
  1039.     mov    DDPio.ddp_command, DDP_CLOSESOCKET
  1040.     mov    bx, offset DDPio
  1041.     call    doATint                ; close the assigned socket
  1042.  
  1043.     jmp    error_ret            ; skip ahead to return
  1044.  
  1045. ;**** LocalTalk PC Card initialized, ready to TSR
  1046. atinit_done:
  1047.     push    cs
  1048.     pop    ds
  1049.     mov    dx, offset end_resident
  1050.     clc
  1051.     ret
  1052.  
  1053. ;**** Got an error while initializing LocalTalk PC Card
  1054. error_wrt:                    ; Display an error message
  1055.     push    cs                ; Get our data segment back
  1056.     pop    ds
  1057.     mov    ah, 9
  1058.     int    21H
  1059.  
  1060. error_ret:                    ; Board not initialized
  1061.     stc
  1062.     ret
  1063.  
  1064.     public    print_parameters
  1065. print_parameters:
  1066. ;echo our command-line parameters
  1067.     ret
  1068.  
  1069. ;*******************************************
  1070.  
  1071. ; Modify ATALK.SYS interrupt number in doATint code (self-modifying code!)
  1072. ATPatch:
  1073.     mov    al, dl                ; Load new interrupt number
  1074.     push    cs
  1075.     pop    es
  1076.     lea    bx, doATint            ; es:bx=offset of doATint code
  1077.     inc    bx                ; skip to operator for INT
  1078.     mov    es:[bx], al            ; modify the code
  1079.     ret
  1080.  
  1081. ; Get ATALK.SYS interrupt number
  1082. ATGetInt:
  1083.     push    cs
  1084.     pop    es
  1085.     lea    bx, doATint            ; es:bx=offset of doATint code
  1086.     inc    bx                ; skip to operator for INT
  1087.     mov    al, es:[bx]            ; load operator for INT
  1088.     xor    ah, ah                ; zero high byte
  1089.     ret                    ; return INT# to caller
  1090.  
  1091. ;*******************************************
  1092.  
  1093. ;*******************************************
  1094.  
  1095. ; caller must set ds:si -> dest for string, dx 16-bit value to sprint
  1096. decstr:
  1097.     mov    di,dx
  1098.     cmp    dx, 0
  1099.     jne    decstr_nonzero
  1100.     mov    al,'0'            ;yes - easier to just print it, than
  1101.     jmp    chrstr            ;  to eliminate all but the last zero.
  1102. decstr_nonzero:
  1103.  
  1104.     xor    ax,ax            ;start with all zeroes in al,bx,bp
  1105.     mov    bp,ax
  1106.  
  1107.     mov    cx,16            ;16 bits in one 16 bit registers.
  1108. decstr_1:
  1109.     rcl    di,1
  1110.     xchg    bp,ax
  1111.     call    addbit
  1112.     xchg    bp,ax
  1113.     adc    al,al
  1114.     daa
  1115.     loop    decstr_1
  1116.  
  1117.     mov    cl,'0'            ;prepare to eliminate leading zeroes.
  1118.     call    bytestr            ;output the first two.
  1119.     mov    ax,bp
  1120.     jmp    wordstr            ;output the next four.
  1121.  
  1122. addbit:    adc    al,al
  1123.     daa
  1124.     xchg    al,ah
  1125.     adc    al,al
  1126.     daa
  1127.     xchg    al,ah
  1128.     ret
  1129.  
  1130. ;print the char in al at ds:bx
  1131. chrstr:
  1132.     mov    byte ptr [bx], al
  1133.     inc    bx
  1134.     ret
  1135.  
  1136. wordstr:
  1137.     push    ax
  1138.     mov    al,ah
  1139.     call    bytestr
  1140.     pop    ax
  1141. bytestr:
  1142.     mov    ah,al
  1143.     shr    al,1
  1144.     shr    al,1
  1145.     shr    al,1
  1146.     shr    al,1
  1147.     call    digstr
  1148.     mov    al,ah
  1149. digstr:
  1150.     and    al,0fh
  1151.     add    al,90h    ;binary digit to ascii hex digit.
  1152.     daa
  1153.     adc    al,40h
  1154.     daa
  1155.     cmp    al,cl            ;leading zero?
  1156.     je    digstr_1
  1157.     mov    cl,-1            ;no more leading zeros.
  1158.     jmp    chrstr
  1159. digstr_1:
  1160.     ret
  1161.  
  1162. ; caller must set ds:dx -> argument string, ds:di -> AddrBlk struct
  1163. print_at_addr:
  1164. ;enter with dx -> dollar terminated name of number, di ->dword.
  1165. ;exit with the number printed and the cursor advanced to the next line.
  1166.     mov    ah,9            ;print the name of the number.
  1167.     int    21h
  1168.     mov    ax, [di].ablk_network    ;print the network number
  1169.     mov    dx, 00H
  1170.     push    di
  1171.     call    decout
  1172.     pop    di
  1173.     mov    al, ':'
  1174.     call    chrout
  1175.     xor    ax, ax
  1176.     mov    al, [di].ablk_nodeid    ; print the nodeid number
  1177.     push    di
  1178.     call    decout
  1179.     pop    di
  1180.     mov    al, ':'
  1181.     call    chrout
  1182.     xor    ax, ax
  1183.     mov    al, [di].ablk_socket    ; print the socket number
  1184.     call    decout
  1185.     mov    al,CR
  1186.     call    chrout
  1187.     mov    al,LF
  1188.     call    chrout
  1189.     ret
  1190.  
  1191. ; caller must set ds:dx -> argument string, ds:di -> 32 bit ip address
  1192. print_ip_addr:
  1193. ;enter with dx -> dollar terminated name of number, di ->dword.
  1194. ;exit with the number printed and the cursor advanced to the next line.
  1195.     mov    ah,9            ;print the name of the number.
  1196.     int    21h
  1197.     mov    al, '['
  1198.     call    chrout
  1199.     xor    ax, ax
  1200.     mov    al, [di]        ;print first byte in decimal.
  1201.     mov    dx, 00H
  1202.     push    di
  1203.     call    decout
  1204.     pop    di
  1205.     mov    al, '.'
  1206.     call    chrout
  1207.     xor    ax, ax
  1208.     mov    al, [di+1]        ; print second byte in decimal
  1209.     push    di
  1210.     call    decout
  1211.     pop    di
  1212.     mov    al, '.'
  1213.     call    chrout
  1214.     xor    ax, ax
  1215.     mov    al, [di+2]        ; print third byte in decimal
  1216.     push    di
  1217.     call    decout
  1218.     pop    di
  1219.     mov    al, '.'
  1220.     call    chrout
  1221.     xor    ax, ax
  1222.     mov    al, [di+3]        ; print fourth byte in decimal
  1223.     call    decout
  1224.     mov    al, ']'
  1225.     call    chrout
  1226.     mov    al,CR
  1227.     call    chrout
  1228.     mov    al,LF
  1229.     call    chrout
  1230.     ret
  1231.  
  1232. code    ends
  1233.  
  1234.     end
  1235.